package com.dhcc.config;

import com.dhcc.userDetailService.ClientUserDetailsServiceImpl;
import com.dhcc.userDetailService.SystemUserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

/**
 * security配置类
 * @ProjectName Dhcc-Cloud
 * @PackageName com.dhcc.config
 * @Title SecurityConfig
 * @Date 2024/10/8 上午1:28
 * @Author LiuGuoting
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    /**
     * 定义管理端权限路径
     */
    private static final String SYSTEM_AUTH_URL = "/auth/system/**";
    /**
     * 定义用户端权限路径
     */
    private static final String CLIENT_AUTH_URL = "/auth/client/**";

    /**
     *定义一个管理端SecurityFilterChain bean，用于配置安全过滤器链
     *配置权限相关的配置
     * 安全框架本质上是一堆的过滤器，称之为过滤器链，每一个过滤器链的功能都不同
     * 设置一些链接不要拦截
     *
     * @param httpSecurity
     * @return SecurityFilterChain
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain systemSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // 仅适用于平台端路径
                .securityMatcher(SYSTEM_AUTH_URL)
                //取消默认登录页面的使用
                .formLogin(AbstractHttpConfigurer::disable)
                //取消默认登出页面的使用
                .logout(AbstractHttpConfigurer::disable)
                // 使用自定义的 AuthenticationManager，支持多个 AuthenticationProvider
                .authenticationManager(systemAuthManager(httpSecurity))
                //禁用csrf保护，前后端分离不需要
                .csrf(AbstractHttpConfigurer::disable)
                //禁用session，因为我们已经使用了JWT
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //禁用httpBasic，因为我们传输数据用的是post，而且请求体是JSON
                .httpBasic(AbstractHttpConfigurer::disable)
                // 开放两个接口，一个注册，一个登录
                .authorizeHttpRequests(request -> request
                        .requestMatchers(HttpMethod.POST, SYSTEM_AUTH_URL).permitAll()
                        .requestMatchers(HttpMethod.GET, SYSTEM_AUTH_URL).permitAll()
                        //其余均要身份认证
                        .anyRequest().authenticated());
        return httpSecurity.build();
    }

    /**
     *定义一个用户端SecurityFilterChain bean，用于配置安全过滤器链
     *配置权限相关的配置
     * 安全框架本质上是一堆的过滤器，称之为过滤器链，每一个过滤器链的功能都不同
     * 设置一些链接不要拦截
     *
     * @param httpSecurity
     * @return SecurityFilterChain
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain clientSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // 仅适用于用户端路径
                .securityMatcher(CLIENT_AUTH_URL)
                //取消默认登录页面的使用
                .formLogin(AbstractHttpConfigurer::disable)
                //取消默认登出页面的使用
                .logout(AbstractHttpConfigurer::disable)
                // 使用自定义的 AuthenticationManager，支持多个 AuthenticationProvider
                .authenticationManager(clientAuthManager(httpSecurity))
                //禁用csrf保护，前后端分离不需要
                .csrf(AbstractHttpConfigurer::disable)
                //禁用session，因为我们已经使用了JWT
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //禁用httpBasic，因为我们传输数据用的是post，而且请求体是JSON
                .httpBasic(AbstractHttpConfigurer::disable)
                // 开放两个接口，一个注册，一个登录
                .authorizeHttpRequests(request -> request
                        .requestMatchers(HttpMethod.POST, CLIENT_AUTH_URL).permitAll()
                        .requestMatchers(HttpMethod.GET, CLIENT_AUTH_URL).permitAll()
                        //其余均要身份认证
                        .anyRequest().authenticated());
        return httpSecurity.build();
    }


    /**
     * 管理端认证管理器
     * @param http
     * @return AuthenticationManager
     * @throws Exception
     */
    @Bean
    @Primary
    public AuthenticationManager systemAuthManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);
        // 注册多个认证提供者
        authenticationManagerBuilder.authenticationProvider(systemAuthenticationProvider());
        // 构建 AuthenticationManager
        return authenticationManagerBuilder.build();
    }

    /**
     * 用户端认证管理器
     * @param http
     * @return AuthenticationManager
     * @throws Exception
     */
    @Bean
    public AuthenticationManager clientAuthManager(HttpSecurity http) throws Exception {
        AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);
        // 注册多个认证提供者
        authenticationManagerBuilder.authenticationProvider(clientAuthenticationProvider());
        // 构建 AuthenticationManager
        return authenticationManagerBuilder.build();
    }

    /**
     * 管理端认证提供者
     * @return AuthenticationProvider
     */
    @Bean
    public AuthenticationProvider systemAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置用户详情服务
        provider.setUserDetailsService(systemUserDetailsService());
        // 设置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        // 返回 DAO 认证提供者
        return provider;
    }

    /**
     * 用户端认证提供者
     * @return AuthenticationProvider
     */
    @Bean
    public AuthenticationProvider clientAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置用户详情服务
        provider.setUserDetailsService(clientUserDetailsService());
        // 设置密码编码器
        provider.setPasswordEncoder(passwordEncoder());
        // 返回 DAO 认证提供者
        return provider;
    }

    /**
     * 管理端用户详情服务
     * @return SystemUserDetailsServiceImpl
     */
    @Bean
    public SystemUserDetailsServiceImpl systemUserDetailsService() {
        // 创建用户详情服务实例
        return new SystemUserDetailsServiceImpl();
    }

    /**
     * 平台端用户详情服务
     * @return ClientUserDetailsServiceImpl
     */
    @Bean
    public ClientUserDetailsServiceImpl clientUserDetailsService() {
        // 创建用户详情服务实例
        return new ClientUserDetailsServiceImpl();
    }

    /**
     * 密码编码器
     * @return BCryptPasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 返回密码编码器
        return new BCryptPasswordEncoder();
    }
}
